#include "stdafx.h"

namespace classifier_test
{

namespace high_order
{

validation_dataset::validation_dataset()
{
}

validation_dataset::validation_dataset(const validation_dataset& Val)
{
}

validation_classifier::validation_classifier()
{
}

validation_classifier::validation_classifier(const validation_classifier& Val)
{
}

holdout_validation_dataset::holdout_validation_dataset()
{
}

holdout_validation_dataset::holdout_validation_dataset(const holdout_validation_dataset& Val)
	: validation_dataset(Val)
{
	Trains = Val.Trains;
	Tests = Val.Tests;
}

holdout_validation_dataset::holdout_validation_dataset(subarray<const record* const> Data)
{
	build(Data);
}

smart_ptr<validation_dataset> holdout_validation_dataset::clone() const
{
	return make_smart_ptr(new holdout_validation_dataset(*this));
}

int holdout_validation_dataset::size() const
{
	return Trains.size() + Tests.size();
}

void holdout_validation_dataset::build(subarray<const record* const> Data)
{
	assert(Data.size() >= 2);
	Trains.assign(Data.begin(), Data.end());
	shuffle(Trains.begin(), Trains.end());
	Tests.assign(Trains.begin() + Trains.size() / 2, Trains.end());
	Trains.erase(Trains.begin() + Trains.size() / 2, Trains.end());
	if (uniform(2) < 1) Trains.swap(Tests);
	pack_vector(Trains);
	pack_vector(Tests);
}

void holdout_validation_dataset::add(validation_dataset* Dataset)
{
	const holdout_validation_dataset& X = dynamic_cast<const holdout_validation_dataset&>(*Dataset);
	Trains.insert(Trains.end(), X.Trains.begin(), X.Trains.end());
	Tests.insert(Tests.end(), X.Tests.begin(), X.Tests.end());
}

smart_ptr<validation_classifier> holdout_validation_dataset::train(classifier* BaseClassifier)
{
	smart_ptr<holdout_validation_classifier> X(new holdout_validation_classifier);
	X->Classifier = BaseClassifier->clone();
	X->Classifier->train(Trains);
	return X;
}

holdout_validation_classifier::holdout_validation_classifier()
{
}

holdout_validation_classifier::holdout_validation_classifier(const holdout_validation_classifier& Val)
	: validation_classifier(Val)
{
	Classifier = Val.Classifier->clone();
}

smart_ptr<validation_classifier> holdout_validation_classifier::clone() const
{
	return make_smart_ptr(new holdout_validation_classifier(*this));
}

double holdout_validation_classifier::test_error(validation_dataset* Dataset) const
{
	const holdout_validation_dataset& X = dynamic_cast<const holdout_validation_dataset&>(*Dataset);
	return classification_error(&*Classifier, X.Tests);
}

void holdout_validation_classifier::test_results(validation_dataset* Dataset, vector<discr_value>& Results) const
{
	const holdout_validation_dataset& X = dynamic_cast<const holdout_validation_dataset&>(*Dataset);
	classification_results(&*Classifier, X.Tests, Results);
}

smart_ptr<classifier> holdout_validation_classifier::get_ensemble_classifier() const
{
	smart_ptr<ensemble_classifier> Val(new ensemble_classifier);
	Val->set_domain(Classifier->get_domain());
	Val->add(&*Classifier);
	return Val;
}

cross_validation_dataset::cross_validation_dataset(int SetGroupCount /*= 0*/)
{
	GroupCount = SetGroupCount;
}

cross_validation_dataset::cross_validation_dataset(const cross_validation_dataset& Val)
	: validation_dataset(Val)
{
	GroupCount = Val.GroupCount;
	DataCount = Val.DataCount;
	Data = Val.Data;
}

cross_validation_dataset::cross_validation_dataset(int SetGroupCount, subarray<const record* const> SetData)
{
	GroupCount = SetGroupCount;
	build(SetData);
}

smart_ptr<validation_dataset> cross_validation_dataset::clone() const
{
	return make_smart_ptr(new cross_validation_dataset(*this));
}

int cross_validation_dataset::size() const
{
	return DataCount;
}

int cross_validation_dataset::get_group_count() const
{
	return GroupCount;
}

void cross_validation_dataset::set_group_count(int Val)
{
	GroupCount = Val;
}

void cross_validation_dataset::build(subarray<const record* const> SetData)
{
	DataCount = SetData.size();
	vector<const record*> P(SetData.begin(), SetData.end());
	shuffle(P.begin(), P.end());
	vector<int> List(GroupCount);
	for (int I = 0; I < GroupCount; I++) List[I] = I;
	shuffle(List.begin(), List.end());
	Data.resize(GroupCount);
	for (int I = 0; I < GroupCount; I++) Data[I].reserve((P.size() + GroupCount - 1) / GroupCount);
	for (int I = 0; I < (int)P.size(); I++) Data[List[I % GroupCount]].push_back(P[I]);
}

void cross_validation_dataset::add(validation_dataset* Dataset)
{
	const cross_validation_dataset& X = dynamic_cast<const cross_validation_dataset&>(*Dataset);
	assert(GroupCount == X.GroupCount);
	DataCount += X.DataCount;
	for (int I = 0; I < GroupCount; I++)
		Data[I].insert(Data[I].end(), X.Data[I].begin(), X.Data[I].end());
}

smart_ptr<validation_classifier> cross_validation_dataset::train(classifier* BaseClassifier)
{
	smart_ptr<cross_validation_classifier> X(new cross_validation_classifier);
	vector<const record*> P;
	P.reserve(DataCount);
	X->Classifiers.resize(GroupCount);
	for (int I = 0; I < GroupCount; I++)
	{
		P.clear();
		for (int J = 0; J < GroupCount; J++)
			if (J != I) P.insert(P.end(), Data[J].begin(), Data[J].end());
		X->Classifiers[I] = BaseClassifier->clone();
		X->Classifiers[I]->train(P);
	}
	return X;
}

cross_validation_classifier::cross_validation_classifier()
{

}

cross_validation_classifier::cross_validation_classifier(const cross_validation_classifier& Val)
	: validation_classifier(Val)
{
	Classifiers.resize(Val.Classifiers.size());
	for (int I = 0; I < (int)Classifiers.size(); I++)
		Classifiers[I] = Val.Classifiers[I]->clone();
}

smart_ptr<validation_classifier> cross_validation_classifier::clone() const
{
	return make_smart_ptr(new cross_validation_classifier(*this));
}

double cross_validation_classifier::test_error(validation_dataset* Dataset) const
{
	const cross_validation_dataset& X = dynamic_cast<const cross_validation_dataset&>(*Dataset);
	assert((int)Classifiers.size() == X.GroupCount);
	int Errors = 0;
	for (int I = 0; I < (int)Classifiers.size(); I++)
		Errors += classification_errors(&*Classifiers[I], X.Data[I]);
	return (double)Errors / X.DataCount;
}

void cross_validation_classifier::test_results(validation_dataset* Dataset, vector<discr_value>& Results) const
{
	const cross_validation_dataset& X = dynamic_cast<const cross_validation_dataset&>(*Dataset);
	assert((int)Classifiers.size() == X.GroupCount);
	Results.clear();
	Results.reserve(Dataset->size());
	for (int I = 0; I < (int)Classifiers.size(); I++)
		for (int J = 0; J < (int)X.Data[I].size(); J++)
			Results.push_back(Classifiers[I]->classify(*X.Data[I][J]));
}

smart_ptr<classifier> cross_validation_classifier::get_ensemble_classifier() const
{
	smart_ptr<ensemble_classifier> Val(new ensemble_classifier);
	Val->set_domain(Classifiers[0]->get_domain());
	for (int I = 0; I < (int)Classifiers.size(); I++)
		Val->add(&*Classifiers[I]);
	return Val;
}

}

}
